#include "GameLogManager.h"
#include "Utils.h"
#include <sqlite3.h>
#include "LogEx.h"
using namespace std;
using namespace SGLib;


CGameLogManager::CGameLogManager()
{
    m_worker = NULL;
    m_isStop = true;
    m_curMonth = GetCurMonth();
    m_lastLogTime = TIME_NOW;
}

CGameLogManager::~CGameLogManager()
{
}

bool CGameLogManager::Start()
{
    if( m_worker )
    {
        return true;
    }

    m_isStop = false;
    m_worker = new CThread( this );
    if( m_worker == NULL || !m_worker->Start() )
    {
        SAFE_DELETE( m_worker );
        return false;
    }
   
    CLogEx::CreateDir( "./gamelog" );
    LOG_INFO( "CGameLogManager Start" );
    return true;
}

void CGameLogManager::Stop()
{
    if( !m_isStop && m_worker )
    {
        m_isStop = true;
        m_workSignal.SetEvent();
        m_worker->Stop();
        SAFE_DELETE( m_worker );
    }
    LOG_INFO( "CGameLogManager Stop" );
}

void CGameLogManager::Run()
{
    LOG_INFO( "CGameLogManager Run" );
    while( !m_isStop )
    {
        m_workSignal.Wait();

        std::deque<_logData> _logQue;
        {
            CGuardLock<CLock> g(m_lock);
            _logQue.swap( m_logQue );
        }
        _DoLog( _logQue );
        LOG_DEBUG( "CGameLogManager log[%d] records", (s32)_logQue.size() );
       
        if( m_isStop )
        {
            break;
        }
    };
}

void CGameLogManager::Check2Log()
{
    // 写日志条件 (1)累积1000条 (2)超过5分钟 (3)隔月
    // 注意不能保证当月的所有log记录能记录在当月的log文件中, 
    // 可能存在部分当月日志记录在下月文件中, 例如09月的N条对局log记录在 10月的日志数据库中
    // 如果采用同步写日历模式可以保证
    u64 now = TIME_NOW;
    string curMonth = GetCurMonth();

    if( curMonth != m_curMonth ||
        QueLength() > E_LogCount_Limit ||
        (now - m_lastLogTime) > E_LogTime_Limit )
    {
        m_lastLogTime = now;
        m_curMonth = curMonth;
        m_workSignal.SetEvent();
    }
}

void CGameLogManager::AddGameLog(u64 userid, s32 gameid, u64 serverid, s32 roomid, u64 battleid, s64 winmoney, s32 tax, s8 isoffline, s8 isrobot, u64 tbegin, u64 tend, std::string &channel, s32 reserve1, s32 reserve2, const char *reserve3)
{
    _logData ld;
    ld.userid = userid;
    ld.gameid = gameid;
    ld.serverid = serverid;
    ld.roomid = roomid;
    ld.battleid = battleid;
    ld.winmoney = winmoney;
    ld.tax = tax;
    ld.isoffline = isoffline;
    ld.isrobot = isrobot;
    ld.tbegin = tbegin;
    ld.tend = tend;
    ld.channel = channel;
    ld.reserve1 = reserve1;
    ld.reserve2 = reserve2;
    if( reserve3 )
    {
        ld.reserve3 = reserve3;
    }
    {
        CGuardLock<CLock> g(m_lock);
        m_logQue.push_back( ld );
    }
    Check2Log();
};

s32 CGameLogManager::QueLength()
{
    s32 count = 0;
    {
        CGuardLock<CLock> g(m_lock);
        count = (s32)m_logQue.size();
    }
    return count;
}

void CGameLogManager::_DoLog(std::deque<_logData> &logQue)
{
    bool createTable = false;
    char dbfile[128] = {0};
    sprintf( dbfile, "./gamelog/gamelog_%s.db", m_curMonth.c_str() ); 
    if( access(dbfile, F_OK) != 0 )
    {
        createTable = true;
    }
   
    sqlite3 *db;
    sqlite3_open( dbfile, &db );
    if( createTable )
    {
        static string createsql = "CREATE TABLE gamelog (idx INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, userid INTEGER NOT NULL DEFAULT 0, gameid INTEGER NOT NULL DEFAULT 0, serverid INTEGER NOT NULL DEFAULT 0, roomid INTEGER NOT NULL DEFAULT 0, battleid INTEGER NOT NULL DEFAULT 0, winmoney INTEGER NOT NULL DEFAULT 0, tax INTEGER NOT NULL DEFAULT 0, isoffline INTEGER NOT NULL DEFAULT 0, isrobot INTEGER NOT NULL DEFAULT 0, begintime TEXT(32) NOT NULL, endtime TEXT(32) NOT NULL, channel TEXT(64) NOT NULL, reserve1 INTEGER DEFAULT 0, reserve2 INTEGER DEFAULT 0, reserve3 TEXT(64));";
        static string idxsql1 = "CREATE INDEX idx1 ON gamelog(userid ASC);";
        static string idxsql2 = "CREATE INDEX idx2 ON gamelog(begintime ASC);";
        sqlite3_exec(db, createsql.c_str(), 0, 0, 0 );
        sqlite3_exec(db, idxsql1.c_str(), 0, 0, 0 );
        sqlite3_exec(db, idxsql2.c_str(), 0, 0, 0 );
    }
    sqlite3_exec(db, "PRAGMA synchronous = OFF;", 0, 0, 0 ); // 关闭写同步
    
    const char *sql = "insert into gamelog values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
    sqlite3_exec( db,"begin;", 0, 0, 0 ); // 开启事务模式
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2( db, sql, strlen(sql), &stmt, 0 );
    std::deque<_logData>::iterator it = logQue.begin();
    for( ; it != logQue.end(); ++it )
    {
        struct tm ut;
        time_t _t = (time_t)(it->tbegin);
        localtime_r( &_t, &ut );
        char strtbegin[64] = {0};
        int tbeginlen = (int)strftime( strtbegin, sizeof(strtbegin), "%Y-%m-%d %H:%M:%S", &ut );
        _t = (time_t)(it->tend);
        localtime_r( &_t, &ut );
        char strtend[64] = {0};
        int tendlen = (int)strftime( strtend, sizeof(strtend), "%Y-%m-%d %H:%M:%S", &ut );
        
        sqlite3_reset( stmt );
        
        sqlite3_bind_null(stmt,  1 );
        sqlite3_bind_int64(stmt, 2, it->userid );
        sqlite3_bind_int(stmt,   3, it->gameid );
        sqlite3_bind_int64(stmt, 4, it->serverid );
        sqlite3_bind_int(stmt,   5, it->roomid);
        sqlite3_bind_int64(stmt, 6, it->battleid );
        sqlite3_bind_int64(stmt, 7, it->winmoney );
        sqlite3_bind_int(stmt,   8, it->tax );
        sqlite3_bind_int(stmt,   9, it->isoffline );
        sqlite3_bind_int(stmt,   10, it->isrobot );
        sqlite3_bind_text( stmt, 11, strtbegin, tbeginlen, NULL );
        sqlite3_bind_text( stmt, 12, strtend, tendlen, NULL );
        sqlite3_bind_text( stmt, 13, it->channel.c_str(), it->channel.length(), NULL );
        sqlite3_bind_int(  stmt, 14, it->reserve1 );
        sqlite3_bind_int(  stmt, 15, it->reserve2 );
        sqlite3_bind_text( stmt, 16, it->reserve3.c_str(), it->reserve3.length(), NULL );
        
        sqlite3_step(stmt);
    }

    sqlite3_finalize( stmt ); 
    sqlite3_exec( db,"commit;", 0, 0, 0 ); 
    sqlite3_close( db );
}


